import 'dart:async';
import 'dart:convert';

import 'package:alipay_kit/src/crypto/rsa.dart';
import 'package:alipay_kit/src/model/alipay_resp.dart';
import 'package:flutter/services.dart';

///
class Alipay {
  ///
  Alipay._();

  static Alipay get instance => _instance;

  static final Alipay _instance = Alipay._();

  static const String _METHOD_ISINSTALLED = 'isInstalled';
  static const String _METHOD_PAY = 'pay';
  static const String _METHOD_AUTH = 'auth';
  static const String _METHOD_USERINFO = 'userInfo';
  static const String _METHOD_GETTOKEN = 'getToken';

  static const String _METHOD_ONPAYRESP = 'onPayResp';
  static const String _METHOD_ONAUTHRESP = 'onAuthResp';

  static const String _ARGUMENT_KEY_ORDERINFO = 'orderInfo';
  static const String _ARGUMENT_KEY_AUTHINFO = 'authInfo';
  static const String _ARGUMENT_KEY_GETTOKENINFO = 'tokenInfo';
  static const String _ARGUMENT_KEY_ISSHOWLOADING = 'isShowLoading';

  static const String SIGNTYPE_RSA = 'RSA';
  static const String SIGNTYPE_RSA2 = 'RSA2';

  static const String AUTHTYPE_AUTHACCOUNT = 'AUTHACCOUNT';
  static const String AUTHTYPE_LOGIN = 'LOGIN';

  late final MethodChannel _channel = const MethodChannel('v7lin.github.io/alipay_kit')..setMethodCallHandler(_handleMethod);

  final StreamController<AlipayResp> _payRespStreamController = StreamController<AlipayResp>.broadcast();
  final StreamController<AlipayResp> _authRespStreamController = StreamController<AlipayResp>.broadcast();

  Future<dynamic> _handleMethod(MethodCall call) async {
    switch (call.method) {
      case _METHOD_ONPAYRESP:
        _payRespStreamController.add(AlipayResp.fromJson((call.arguments as Map<dynamic, dynamic>).cast<String, dynamic>()));
        break;
      case _METHOD_ONAUTHRESP:
        _authRespStreamController.add(AlipayResp.fromJson((call.arguments as Map<dynamic, dynamic>).cast<String, dynamic>()));
        break;
    }
  }

  /// 支付
  Stream<AlipayResp> payResp() {
    return _payRespStreamController.stream;
  }

  /// 登录
  Stream<AlipayResp> authResp() {
    return _authRespStreamController.stream;
  }

  /// 检测支付宝是否已安装 - x.y.z-Android-Only 版本下 iOS 调用会直接抛出异常 No implementation [MissingPluginException]
  Future<bool> isInstalled() async {
    return await _channel.invokeMethod<bool?>(_METHOD_ISINSTALLED) ?? false;
  }

  /// 支付
  Future<void> payOrderJson({
    required String orderInfo,
    String signType = SIGNTYPE_RSA2,
    bool isShowLoading = true,
  }) {
    return _channel.invokeMethod<void>(
      _METHOD_PAY,
      <String, dynamic>{
        _ARGUMENT_KEY_ORDERINFO: orderInfo,
        _ARGUMENT_KEY_ISSHOWLOADING: isShowLoading,
      },
    );
  }

  /// 登录
  Future<void> auth({
    required String appId, // 支付宝分配给开发者的应用ID
    required String pid, // 签约的支付宝账号对应的支付宝唯一用户号，以2088开头的16位纯数字组成
    required String targetId, // 商户标识该次用户授权请求的ID，该值在商户端应保持唯一
    String authType = AUTHTYPE_AUTHACCOUNT, // 标识授权类型，取值范围：AUTHACCOUNT 代表授权；LOGIN 代表登录
    bool isShowLoading = true,
  }) {
    assert(authType == AUTHTYPE_AUTHACCOUNT || authType == AUTHTYPE_LOGIN);
    final Map<String, dynamic> authInfo = <String, dynamic>{
      'apiname': 'com.alipay.account.auth',
      'method': 'alipay.open.auth.sdk.code.get',
      'app_id': appId,
      'app_name': 'chege',
      'biz_type': 'openservice',
      'pid': pid,
      'product_id': 'APP_FAST_LOGIN',
      'scope': 'kuaijie',
      'target_id': targetId,
      'auth_type': authType,
    };
    final Encoding encoding = utf8; // utf-8
    final String param = _param(authInfo, encoding);
    return authSign(
      info: '$param',
      isShowLoading: isShowLoading,
    );
  }

  /// 登录 - x.y.z-Android-Only 版本下 iOS 调用会直接抛出异常 No implementation [MissingPluginException]
  Future<void> authSign({
    required String info,
    bool isShowLoading = true,
  }) {
    return _channel.invokeMethod<void>(
      _METHOD_AUTH,
      <String, dynamic>{
        _ARGUMENT_KEY_AUTHINFO: info,
        _ARGUMENT_KEY_ISSHOWLOADING: isShowLoading,
      },
    );
  }

  String _param(Map<String, dynamic> map, Encoding encoding) {
    return map.entries.map((MapEntry<String, dynamic> e) => '${e.key}=${Uri.encodeQueryComponent('${e.value}', encoding: encoding)}').join('&');
  }

  String _sign(Map<String, dynamic> map, String signType, String privateKey) {
    // 参数排序
    final List<String> keys = map.keys.toList();
    keys.sort();
    final String content = keys.map((String e) => '$e=${map[e]}').join('&');
    String sign;
    switch (signType) {
      case SIGNTYPE_RSA:
        sign = base64.encode(RsaSigner.sha1Rsa(privateKey).sign(utf8.encode(content)));
        break;
      case SIGNTYPE_RSA2:
        sign = base64.encode(RsaSigner.sha256Rsa(privateKey).sign(utf8.encode(content)));
        break;
      default:
        throw UnsupportedError('Alipay sign_type($signType) is not supported!');
    }
    return sign;
  }
}
